/*
 * C/C++ KLIB Project
 * 
 * Copyright (c) 2012 Ken Zhang (cpp.china@gmail.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 *
 */

#include "var.h"
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

var_t var_char(char value)
{
	var_t var;
	var.datatype = DATA_TYPE_CHAR;
	var.value.charVal = value;
	return var;
}

var_t var_uchar(unsigned char value)
{
	var_t var;
	var.datatype = DATA_TYPE_BYTE;
	var.value.byteVal = value;
	return var;
}

var_t var_short(short value)
{
	var_t var;
	var.datatype = DATA_TYPE_SHORT;
	var.value.shortVal = value;
	return var;
}

var_t var_ushort(unsigned short value)
{
	var_t var;
	var.datatype = DATA_TYPE_USHORT;
	var.value.ushortVal = value;
	return var;
}

var_t var_int(int value)
{
	var_t var;
	var.datatype = DATA_TYPE_INT;
	var.value.intVal = value;
	return var;
}

var_t var_uint(unsigned int value)
{
	var_t var;
	var.datatype = DATA_TYPE_UINT;
	var.value.uintVal = value;
	return var;
}

var_t var_long(long value)
{
	var_t var;
	var.datatype = DATA_TYPE_LONG;
	var.value.longVal = value;
	return var;
}

var_t var_ulong(unsigned long value)
{
	var_t var;
	var.datatype = DATA_TYPE_ULONG;
	var.value.ulongVal = value;
	return var;
}

var_t var_longlong(long long value)
{
	var_t var;
	var.datatype = DATA_TYPE_LONGLONG;
	var.value.longlongVal = value;
	return var;
}

var_t var_time(time_t value)
{
	var_t var;
	var.datatype = DATA_TYPE_TIME;
	var.value.timeVal = value;
	return var;
}

var_t var_size(size_t value)
{
	var_t var;
	var.datatype = DATA_TYPE_SIZE;
	var.value.sizeVal = value;
	return var;
}

var_t var_float(float value)
{
	var_t var;
	var.datatype = DATA_TYPE_FLOAT;
	var.value.floatVal = value;
	return var;
}

var_t var_double(double value)
{
	var_t var;
	var.datatype = DATA_TYPE_DOUBLE;
	var.value.doubleVal = value;
	return var;
}

var_t var_longdouble(long double value)
{
	var_t var;
	var.datatype = DATA_TYPE_LONGDOUBLE;
	var.value.longdoubleVal = value;
	return var;
}

var_t var_cstr(char *value)
{
	var_t var;
	var.datatype = DATA_TYPE_CSTR;
	var.value.cstrVal = value;
	return var;
}

var_t var_ptr(void *value)
{
	var_t var;
	var.datatype = DATA_TYPE_PTR;
	var.value.ptrVal = value;
	return var;
}

var_t *var_create(var_t value)
{
	var_t *p = malloc(sizeof(var_t));
	memcpy(p, &value, sizeof(var_t));
	return p;
}

int var_compare(var_t *v1, var_t *v2)
{
	#define VALUE_COMPARE(v1, v2)	(v1 > v2 ? 1 : (v1 < v2 ? -1 : 0))

	if (v1 == v2) {
		return 0;
	}

	if (v1 == NULL || v2 == NULL) {
		_set_errno(EINVAL);
		return EINVAL;
	}

	if (v1->datatype != v2->datatype) {
		_set_errno(EINVAL);
		return EINVAL;
	}

	switch (v1->datatype) {
		case DATA_TYPE_CHAR:
			return VALUE_COMPARE(v1->value.charVal, v2->value.charVal);
		case DATA_TYPE_BYTE:
			return VALUE_COMPARE(v1->value.byteVal, v2->value.byteVal);
		case DATA_TYPE_SHORT:
			return VALUE_COMPARE(v1->value.shortVal, v2->value.shortVal);
		case DATA_TYPE_USHORT:
			return VALUE_COMPARE(v1->value.ushortVal, v2->value.ushortVal);
		case DATA_TYPE_INT:
			return VALUE_COMPARE(v1->value.intVal, v2->value.intVal);
		case DATA_TYPE_UINT:
			return VALUE_COMPARE(v1->value.uintVal, v2->value.uintVal);
		case DATA_TYPE_LONG:
			return VALUE_COMPARE(v1->value.longVal, v2->value.longVal);
		case DATA_TYPE_ULONG:
			return VALUE_COMPARE(v1->value.ulongVal, v2->value.ulongVal);
		case DATA_TYPE_LONGLONG:
			return VALUE_COMPARE(v1->value.longlongVal, v2->value.longlongVal);
		case DATA_TYPE_TIME:
			return VALUE_COMPARE(v1->value.timeVal, v2->value.timeVal);
		case DATA_TYPE_SIZE:
			return VALUE_COMPARE(v1->value.sizeVal, v2->value.sizeVal);
		case DATA_TYPE_FLOAT:
			return VALUE_COMPARE(v1->value.floatVal, v2->value.floatVal);
		case DATA_TYPE_DOUBLE:
			return VALUE_COMPARE(v1->value.doubleVal, v2->value.doubleVal);
		case DATA_TYPE_LONGDOUBLE:
			return VALUE_COMPARE(v1->value.longdoubleVal, v2->value.longdoubleVal);
		case DATA_TYPE_CSTR:
			return strcmp(v1->value.cstrVal, v2->value.cstrVal);
		case DATA_TYPE_PTR:
			return VALUE_COMPARE(v1->value.ptrVal, v2->value.ptrVal);
		default:
			_set_errno(EINVAL);
			return EINVAL;
	}
	return EINVAL;
}

const char *tostring(var_t *var)
{
	static char buffer[256];

	switch (var->datatype) {
		case DATA_TYPE_CHAR:
			sprintf(buffer, "%c", var->value.charVal);
			break;
		case DATA_TYPE_BYTE:
			sprintf(buffer, "%d", var->value.byteVal);
			break;
		case DATA_TYPE_SHORT:
			sprintf(buffer, "%hd", var->value.shortVal);
			break;
		case DATA_TYPE_USHORT:
			sprintf(buffer, "%hd", var->value.ushortVal);
			break;
		case DATA_TYPE_INT:
			sprintf(buffer, "%d", var->value.intVal);
			break;
		case DATA_TYPE_UINT:
			sprintf(buffer, "%u", var->value.uintVal);
			break;
		case DATA_TYPE_LONG:
			sprintf(buffer, "%ld", var->value.longVal);
			break;
		case DATA_TYPE_ULONG:
			sprintf(buffer, "%lu", var->value.ulongVal);
			break;
		case DATA_TYPE_LONGLONG:
			sprintf(buffer, "%lld", var->value.longlongVal);
			break;
		case DATA_TYPE_TIME:
			tmformat(buffer, sizeof(buffer), "yyyy-mm-dd HH:MM:SS", localtime(&var->value.timeVal));
			break;
		case DATA_TYPE_SIZE:
			sprintf(buffer, "%Iu", var->value.sizeVal);
			break;
		case DATA_TYPE_FLOAT:
			sprintf(buffer, "%f", (double)var->value.floatVal);
			break;
		case DATA_TYPE_DOUBLE:
			sprintf(buffer, "%f", var->value.doubleVal);
			break;
		case DATA_TYPE_LONGDOUBLE:
			sprintf(buffer, "%lf", var->value.longdoubleVal);
			break;
		case DATA_TYPE_CSTR:
			_snprintf(buffer, sizeof(buffer) - 1, "%s", var->value.cstrVal);
			break;
		case DATA_TYPE_PTR:
			sprintf(buffer, "%p", var->value.ptrVal);
			break;
		default:
			return NULL;
	}
	return buffer;
}
